package org.jaaksi.kottlindemo.coroutines

import kotlinx.coroutines.*
import timber.log.Timber

object TestException {
    /**
     * 协程一旦接收到异常，它就会取消自己，并将异常传递给它的父协程（launch）。
     * 父协程取消自身及所有子协程，然后将异常传递给父协程（runBlocking）。
     * runBlocking是一个根协程（它没有父类），所以它做的就是结束程序（runBlocking 会重新抛出异常）
     * 因此，如果异常没有被捕获而不停的传递，那么层次结构中的所有协程都将会被取消。
     *
     * 在异常破坏协程之前捕获异常是有帮助的。异常信息是通过 job 进行的，因此使用 try-catch 包装在协程构造器上没有任何帮助。
     */
    fun testException() = runBlocking {
        // 1111
        // 3333
        // 2222无法输出
        launch {
            delay(1000)
            Timber.e("1111")
            delay(2000)
            Timber.e("2222")
        }
        // try { launch{} } catch(ex){}  这样是无效的。异常信息是通过 job 进行的
        launch {
            delay(1100)
            Timber.e("3333")
            throw IllegalAccessException("子协程抛出异常")
        }
    }

    fun testException2() {
        val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
            Timber.e(throwable)
        }
        // 协程上下文指定SupervisorJob时，该协程中的异常不会向外传播，因此不会影响其父亲/兄弟协程，也不会被其兄弟协程抛出的异常影响。Job不可以
        val coroutineScope = CoroutineScope(SupervisorJob() + exceptionHandler)
        with(coroutineScope) {
            launch {
                delay(1000)
                Timber.e("1111")
                delay(2000)
                Timber.e("2222")
            }
            launch {
                delay(1100)
                Timber.e("3333")
                throw IllegalAccessException("子协程抛出异常")
            }
        }
    }

    fun testException3() {
        // 另一种停止异常传递的方法是使用 supervisorScope 包装协程构建器。这是非常方便的，因为我们仍然保持与父节点的连接，而任何来自协程的异常都将被忽略。
        val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
            Timber.e(throwable)
        }
        CoroutineScope(Job()).launch(exceptionHandler) {
            supervisorScope {
                launch {
                    delay(1000)
                    Timber.e("1111")
                    delay(2000)
                    Timber.e("2222")
                }
                launch {
                    delay(1100)
                    Timber.e("3333")
                    throw IllegalAccessException("子协程抛出异常")
                }
            }
        }
    }
}